#include <windows.h>
#include <stdio.h>

#include "lib.h"
#include "filesystem.h"
using namespace Library;

/*
  FROM ARTICLE: http://support.microsoft.com/default.aspx?kbid=231497


  Determines the amount of free space available for the caller.
  Runs on Windows 95 retail and later, and on Windows 4.0 and later.  
  Uses GetDiskFreeSpaceEx if available, otherwise reverts to
  GetDiskFreeSpace.

  To determine the amount of available space correctly:

   * Use 64-bit math with the return values of both GetDiskFreeSpace
     and GetDiskFreeSpaceEx so that you can determine the sizes of
     volumes that are larger than 2GB.

  Programs that need to determine how much free space the current user
  can have (such as whether there is enough space to complete an
  installation) have an additional requirement:

   * Use the lpFreeBytesAvailableToCaller value from
     GetDiskFreeSpaceEx rather than lpTotalNumberOfFreeBytes.  
     This is because Windows 2000 has disk quota management that
     administrators may use to limit the amount of disk space that
     users may use.
*/ 


typedef BOOL (WINAPI *P_GDFSEA)(LPCSTR, PULARGE_INTEGER, 
                                 PULARGE_INTEGER, PULARGE_INTEGER);

typedef BOOL (WINAPI *P_GDFSEW)(LPCWSTR, PULARGE_INTEGER, 
                                 PULARGE_INTEGER, PULARGE_INTEGER);

//----------------------------------------------------------------------------
// Public
//
bool8
FileSystem::freeSpace(const uint16 * dir, uint64 * total, uint64 * available)
{
	BOOL  fResult=FALSE;

	DWORD dwSectPerClust,
		dwBytesPerSect,
		dwFreeClusters,
		dwTotalClusters;

	P_GDFSEW pGetDiskFreeSpaceExW = NULL;
	P_GDFSEA pGetDiskFreeSpaceExA = NULL;

	unsigned __int64 i64FreeBytesToCaller = 0,
		i64TotalBytes = 0,
		i64FreeBytes = 0;
	/*
		Use GetDiskFreeSpaceEx if available; otherwise, use
		GetDiskFreeSpace.

		Note: Since GetDiskFreeSpaceEx is not in Windows 95 Retail, we
		dynamically link to it and only call it if it is present.  We 
		don't need to call LoadLibrary on KERNEL32.DLL because it is 
		already loaded into every Win32 process's address space.
	*/ 
	pGetDiskFreeSpaceExA = (P_GDFSEA)GetProcAddress (
		GetModuleHandle (TEXT("kernel32.dll")),
		"GetDiskFreeSpaceExA");
	pGetDiskFreeSpaceExW = (P_GDFSEW)GetProcAddress (
		GetModuleHandle (TEXT("kernel32.dll")),
		"GetDiskFreeSpaceExW");

	OSVERSIONINFO vi;
	vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	if (GetVersionEx( &vi ) && 
		vi.dwPlatformId == VER_PLATFORM_WIN32_NT && 
		pGetDiskFreeSpaceExW)
	{
		fResult = pGetDiskFreeSpaceExW (dir,
			(PULARGE_INTEGER)&i64FreeBytesToCaller,
			(PULARGE_INTEGER)&i64TotalBytes,
			(PULARGE_INTEGER)&i64FreeBytes);
	}
	else if (pGetDiskFreeSpaceExA)
	{
		char path[MAX_PATH + 1] = { '\0' };
		StringConversions::convert(path, dir, MAX_PATH);

		fResult = pGetDiskFreeSpaceExA (path,
			(PULARGE_INTEGER)&i64FreeBytesToCaller,
			(PULARGE_INTEGER)&i64TotalBytes,
			(PULARGE_INTEGER)&i64FreeBytes);
	}
	else
	{
		fResult = GetDiskFreeSpace (dir, 
			&dwSectPerClust,
			&dwBytesPerSect, 
			&dwFreeClusters,
			&dwTotalClusters);

		if (fResult)
		{
			/* force 64-bit math */ 
			i64TotalBytes = (__int64)dwTotalClusters * dwSectPerClust *
				dwBytesPerSect;
			i64FreeBytes = (__int64)dwFreeClusters * dwSectPerClust *
				dwBytesPerSect;

            // kharmon.  11-01-05.  #6566.  Assume that all free
            // space is available to the caller, otherwise this 
            // path will alwyas result in i64FreeBytesToCaller being zero.
            i64FreeBytesToCaller = i64FreeBytes;
		}
	}

		
	if (fResult)
	{
		if (total)
			*total = i64TotalBytes;
		
		if (available)
			*available = i64FreeBytesToCaller;
		
		return true;
	}
	
	return false;
}

uint32
FileSystem::maxPath()
{
	return MAX_PATH;
}
